package animation.sort.gui;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
import javax.swing.event.ChangeEvent;

import animation.core.AnimationController;
import animation.sort.SortAnimation;
import animation.sort.algorithms.monitoring.MonitorModelFactory;

import static animation.core.AnimationController.AnimationCommand.*;

public class AnimationDisplayArea
        extends JComponent
        implements AnimationController
{

    private Dimension minSize;
    private SortAnimation sortAnimation;
    private SortToolBar toolBar;
    private static final int FRAME_SPACE = 5;
    private JComboBox algorithmSelector;
    private Main main;
    private MonitoringArea monitoringArea;
    private AnimationState state;

    AnimationDisplayArea(Main main, SortToolBar toolBar, JComboBox algorithmSelector, int panels, MonitoringArea monitoringArea)
    {
        this.main = main;
        minSize = new Dimension(700, 500 / panels);
        this.toolBar = toolBar;
        this.algorithmSelector = algorithmSelector;
        this.state = AnimationState.STOPED;
        this.monitoringArea = monitoringArea;
        setMinimumSize(minSize);
        setBorder(BorderFactory.createLoweredBevelBorder());
        setBackground(Color.WHITE);
        setOpaque(true);
        toolBar.setControler(this);
    }

    public Dimension getPreferredSize()
    {
        return minSize;
    }


    @Override
    public void repaint() {
        super.repaint();
        monitoringArea.repaint();
    }

    protected void paintComponent(Graphics g)
    {
        g.setColor(Color.WHITE);
        final int width = getWidth();
        final int height = getHeight();
        g.fillRect(0, 0, width, height);
        if (sortAnimation != null) {
            sortAnimation.paintArray((Graphics2D) g.create(FRAME_SPACE, FRAME_SPACE, width - 2 * FRAME_SPACE,
                                                           height - 2 * FRAME_SPACE));
        }
    }

    public Dimension getDisplayAreaSize()
    {
        return getSize();
    }


    public void end()
    {
        state.performAction(this, STOP);
        /*if (!main.anyRunning()) {
            toolBar.enableControls(isRunning());
        }
        algorithmSelector.setEnabled(!isRunning());*/
    }

    public void pause()
    {
        toolBar.setPaused();
    }

    public void itemStateChanged(ItemEvent e)
    {
        if (e.getStateChange() == ItemEvent.SELECTED) {
            initAnimation();
        }
    }

    public void actionPerformed(ActionEvent e)
    {
        String cmd = e.getActionCommand();
        AnimationCommand command = AnimationCommand.valueOf(cmd);

        state.performAction(this, command);

        /*if (main.anyRunning() == isRunning()) {
            toolBar.enableControls(isRunning());
            algorithmSelector.setEnabled(!isRunning());
        }*/
    }

    void initAnimation()
    {
        sortAnimation = getSortAlgorithm();
        sortAnimation.init(this, (getSize().width - 2 * FRAME_SPACE - 4) / 16);
        sortAnimation.setModel(toolBar.getModel());
        monitoringArea.setModel(MonitorModelFactory.createModel(sortAnimation));
    }

    public SortAnimation getSortAlgorithm()
    {
        SortAnimation animation = (SortAnimation) algorithmSelector.getSelectedItem();
        SortAnimation clone = null;
        try {
            clone = (SortAnimation)animation.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }


    public void stateChanged(ChangeEvent e)
    {
        JSlider slider = toolBar.getSlider();
        if (sortAnimation != null && e.getSource() == slider) {
            sortAnimation.setSpeed(slider.getValue());
        }
    }

    public boolean isRunning()
    {
        return state == AnimationState.RUNNING || state == AnimationState.PAUSED;
    }


    private enum AnimationState {
        RUNNING{
            void initState(AnimationDisplayArea controller) {
                controller.toolBar.setRunning();
                controller.toolBar.disableControls();
                controller.algorithmSelector.setEnabled(false);
            }

            public void performAction(AnimationDisplayArea controller, AnimationCommand command) {
                validateState(controller);

                if (command == RUN){
                    controller.sortAnimation.togglePause();
                    changeState(controller, PAUSED);

                } else if (command == STOP){
                    if (controller.sortAnimation != null){
                        controller.sortAnimation.doStop();
                        changeState(controller, STOPED);
                    }
                }
            }
        },
        STOPED{

            void initState(AnimationDisplayArea controller) {
                controller.algorithmSelector.setEnabled(true);
                if (controller.main.anyRunning()){
                    // ignore if there is still someone running
                }else{
                    controller.toolBar.setStoped();
                    controller.toolBar.enableControls();
                }
            }

            public void performAction(AnimationDisplayArea controller, AnimationCommand command) {
                if (command == RUN){
                    controller.initAnimation();
                    controller.sortAnimation.setSpeed(controller.toolBar.getSlider().getValue());
                    controller.sortAnimation.start();
                    changeState(controller, RUNNING);
                } else if (command == STEP){
                    controller.initAnimation();
                    controller.sortAnimation.setSpeed(controller.toolBar.getSlider().getValue());
                    controller.sortAnimation.togglePause();
                    controller.sortAnimation.start();
                    changeState(controller, PAUSED);
                }
            }},
        PAUSED{
            void initState(AnimationDisplayArea controller) {
                controller.toolBar.setPaused();
            }

            public void performAction(AnimationDisplayArea controller, AnimationCommand command) {
                validateState(controller);

                if (command == RUN){
                    controller.sortAnimation.togglePause();
                    changeState(controller, RUNNING);
                } else if (command == STOP){
                    controller.sortAnimation.doStop();
                    changeState(controller, STOPED);
                } else if (command == STEP){
                    controller.sortAnimation.step();
                }
            }};

        private static void validateState(AnimationDisplayArea controller) {
            if (controller.sortAnimation == null){
                throw new IllegalStateException("Sorter Animation not set!");
            }
        }

        abstract void initState(AnimationDisplayArea controller);

        private static void changeState(AnimationDisplayArea controller, AnimationState newState) {
            controller.state = newState;
            controller.state.initState(controller);
        }


        public abstract void performAction(AnimationDisplayArea controller, AnimationCommand command);
    }




}
